////////////////////////////////
//tableparse.lua
//Saves and loads strings to tables. These are helper functions for the stuff in funcdefine
//DEFINES
////////////////////////////////

//This will parse a table into a string, indent is for recursiveness.
function bbb.tabletostring(tbl,indent)
	if(tbl==nil) then //No arguments passed
		bbb.error("TABLE PARSE","No table passed.")
		return false
	end
	//Parse this out
	if(indent==nil) then
		indent=0
	end
	local kstr,vtyp,vval,vidx,vlen,output,indentstr,out
	indentstr=string.rep(" ",indent) //Prettification method
	out=""
	for k,v in pairs(tbl) do //For every key/value pair in the table...
		if(type(k)=="string") then
			kstr=k.."s" //Append a "s" if it is a string based key
		else
			kstr=tostring(k).."n" //And a n if it is a numerical one.
		end
		vtyp=type(v) //The type of the value
		if(vtyp=="number") then //This will convert numbers to floats or ints.
			if(math.floor(v)==v) then
				vtyp="int"
			else
				vtyp="float"
			end
		end
		vidx=bbb.t[vtyp] //Get the index of the type.
		if(vidx==nil) then //The type system doesn't understand this type.
			bbb.error("TABLE PARSE","Unknown type: "..vtyp)
			return false
		end
		vval=bbb.types[vidx].tostring(v,indent+1) //See this cleverness? Hells yeah. Recursive.
		
		//If there is an error, this will allow the recursion to break apart, so it doesn't think it has good data.
		if(vval==false) then
			return false
		end
		//Let me explain. This function is the same as bbb.types["table"].tostring
		//The indent thing allows the table to become indented. If the tostring function doesn't need it, it will be harmlessly discarded.
		vlen=tostring(string.len(vval)) //This is useful for sql.
		if(vtyp!="table") then //Tables need slightly different handling with newlines and indent.
			out=out..indentstr..'<'..kstr..' t="'..vtyp..'" l="'..vlen..'">'..vval..'</'..kstr..'>\n'
		else
			out=out..indentstr..'<'..kstr..' t="'..vtyp..'" l="'..vlen..'">\n'..vval..indentstr..'</'..kstr..'>\n'
		end
	end
	return out
end

function bbb.stringtotable(str)
	if(type(str)!="string") then
		bbb.error("STRING TO TABLE","Bad input.")
		return false
	end
	local xmltable=bbb.collectxml(str)
	if(xmltable==false) then
		bbb.error("STRING TO TABLE","Bad xml table.")
		return false
	end
	local info
	xmltable,info=bbb.parsexmltable(xmltable)
	if(xmltable==false) then
		bbb.error("STRING TO TABLE","Bad parsed xml table.")
		return false
	end
	return xmltable,info
end

//The xmlcollect funtion returns the table in a funky way, this will parse it back to a happy state.
function bbb.parsexmltable(tbl)
	if(type(tbl)!="table") then
		bbb.error("XML TABLE PARSE","Bad input.")
		return false
	end
	//Tbl is the non formatted table.
	local key,vtype,out,info,vidx
	out={}
	info={}
	for k,v in ipairs(tbl) do
		key=v.label
		if(string.sub(key,-1)=="s") then //This will handle string indexes
			key=string.sub(key,1,-2)
		else //Numerical
			key=tonumber(string.sub(key,1,-2))
		end
		info[key]={}
		info[key]._t=v.xarg.t //Type
		vidx=bbb.t[v.xarg.t] //Get the index of the type.
		if(vidx==nil) then //The type system doesn't understand this type.
			bbb.error("XML TABLE PARSE","Unknown type: "..v.xarg.t)
			return false
		end
		if(v.xarg.t=="table") then //It is a table.
			out[key],info[key]=bbb.parsexmltable(v)
		else
			info[key]._l=v.xarg.l //Length, tables don't need it.
			out[key]=bbb.types[vidx].fromstring(v[1]) //XML files can contan multiple entries for one key. This can't.
		end
	end
	return out,info
end

//http://lua-users.org/wiki/LuaXml
//Roberto Ierusalimschy 
function bbb.parseargs(s)
  local arg = {}
  string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
    arg[w] = a
  end)
  return arg
end

//I have no idea how this works, it just does.
function bbb.collectxml(s)
  if(type(s)!="string") then
	bbb.error("COLLECT XML","Bad input given.")
	return false
  end
  local stack = {}
  local top = {}
  table.insert(stack, top)
  local ni,c,label,xarg, empty
  local i, j = 1, 1
  while true do
    ni,j,c,label,xarg, empty = string.find(s, "<(%/?)(%w+)(.-)(%/?)>", j)
    if not ni then break end
    local text = string.sub(s, i, ni-1)
    if not string.find(text, "^%s*$") then
      table.insert(top, text)
    end
    if empty == "/" then  -- empty element tag
      table.insert(top, {label=label, xarg=bbb.parseargs(xarg), empty=1})
    elseif c == "" then   -- start tag
      top = {label=label, xarg=bbb.parseargs(xarg)}
      table.insert(stack, top)   -- new level
    else  -- end tag
      local toclose = table.remove(stack)  -- remove top
      top = stack[#stack]
      if #stack < 1 then
        bbb.error("COLLECT XML","nothing to close with "..label)
      end
      if toclose.label ~= label then
        bbb.error("COLLECT XML","trying to close "..toclose.label.." with "..label)
      end
      table.insert(top, toclose)
    end
    i = j+1
  end
  local text = string.sub(s, i)
  if not string.find(text, "^%s*$") then
    table.insert(stack[stack.n], text)
  end
  if #stack > 1 then
    bbb.error("COLLECT XML","unclosed "..stack[stack.n].label)
  end
  return stack[1]
end

//Test function to ensure that everything is hokey dory.
function bbb.testtablestring()
	local origtable=
	{
		stringvalue="HI THERE",
		intvalue=14,
		floatvalue=15.26,
		tablevalue={
			stringvalue2="HI THERE 2",
			intvalue2=15,
			floatvalue2=15.27
		},
		indexedtable={"String Indexed",15.25,13}
	}
	bbb.print("Origional table:")
	PrintTable(origtable)
	bbb.print("Generated string:")
	local xmlstring=bbb.tabletostring(origtable)
	bbb.print(xmlstring)
	bbb.print("Generated table from string:")
	local gentable=bbb.stringtotable(xmlstring)
	PrintTable(gentable)
	bbb.print("Generated string from generated table:")
	local gentablestring=bbb.tabletostring(gentable)
	bbb.print(gentablestring)
	
	if(gentablestring==xmlstring) then //I will be amazed if this works:
		bbb.print("Holy balony, batman! They're equal!")
	end
	//The table will usually become out of order, and if it does the strings will not be the same, but the tables will.
end
